﻿using System;
using System.Collections.Generic;
using System.Drawing;
using rrd4n.Common;

namespace rrd4n.Graph
{
   /* ============================================================
    * Rrd4n : Pure c# implementation of RRDTool's functionality
    * ============================================================
    *
    * Project Info:  http://minidev.se
    * Project Lead:  Mikael Nilsson (info@minidev.se)
    *
    * Developers:    Mikael Nilsson
    *
    *
    * (C) Copyright 2009-2010, by Mikael Nilsson.
    *
    * This library is free software; you can redistribute it and/or modify it under the terms
    * of the GNU Lesser General Public License as published by the Free Software Foundation;
    * either version 2.1 of the License, or (at your option) any later version.
    *
    * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
    * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    * See the GNU Lesser General Public License for more details.
    *
    * You should have received a copy of the GNU Lesser General Public License along with this
    * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
    * Boston, MA 02111-1307, USA.
    */

   /**
    * Class which should be used to define new Rrd4n graph. Once constructed and populated with data
    * object of this class should be passed to the constructor of the {@link RrdGraph} class which
    * will actually create the graph.
    * <p/>
    * The text printed below the actual graph can be formated by appending
    * special escaped characters at the end of a text. When ever such a
    * character occurs, all pending text is pushed onto the graph according to
    * the character specified.
    * <p/>
    * Valid markers are: \j for justified, \l for left aligned, \r for right
    * aligned and \c for centered.
    * <p/>
    * Normally there are two space characters inserted between every two
    * items printed into the graph. The space following a string can be
    * suppressed by putting a \g at the end of the string. The \g also squashes
    * any space inside the string if it is at the very end of the string.
    * This can be used in connection with %s to suppress empty unit strings.
    * <p/>
    * A special case is COMMENT:\s this inserts some additional vertical
    * space before placing the next row of legends.
    * <p/>
    * When text has to be formated without special instructions from your
    * side, RRDTool will automatically justify the text as soon as one string
    * goes over the right edge. If you want to prevent the justification
    * without forcing a newline, you can use the special tag \J at the end of
    * the string to disable the auto justification.
    */
   public class RrdGraphDef : RrdGraphConstants
   {
      internal bool poolUsed = false; // ok
      internal bool antiAliasing = false; // ok
      internal String filename = RrdGraphConstants.IN_MEMORY_IMAGE; // ok
      internal long startTime; // ok
      internal long endTime; // ok
      internal TimeAxisSetting timeAxisSetting = null; // ok
      internal ValueAxisSetting valueAxisSetting = null; // ok
      internal bool altYGrid = false; // ok
      internal bool noMinorGrid = false; // ok
      internal bool altYMrtg = false; // ok
      internal bool altAutoscale = false; // ok
      internal bool altAutoscaleMax = false; // ok
      internal int unitsExponent = int.MaxValue; // ok
      internal int unitsLength = DEFAULT_UNITS_LENGTH; // ok
      internal String verticalLabel = null; // ok
      internal int width = DEFAULT_WIDTH; // ok
      internal int height = DEFAULT_HEIGHT; // ok
      bool interlaced = false; // ok
      internal String imageInfo = null; // ok
      internal String imageFormat = DEFAULT_IMAGE_FORMAT; // ok
      internal float imageQuality = DEFAULT_IMAGE_QUALITY; // ok
      internal String backgroundImage = null; // ok
      internal String overlayImage = null; // ok
      public String unit = null; // ok
      internal bool lazy = false; // ok
      internal double minValue = Double.NaN; // ok
      internal double maxValue = Double.NaN; // ok
      internal bool rigid = false; // ok
      internal double scaleBase = DEFAULT_BASE;  // ok
      internal bool logarithmic = false; // ok

      internal Color[] colors = new Color[]{
		// ok
		DEFAULT_CANVAS_COLOR,
		DEFAULT_BACK_COLOR,
		DEFAULT_SHADEA_COLOR,
		DEFAULT_SHADEB_COLOR,
		DEFAULT_GRID_COLOR,
		DEFAULT_MGRID_COLOR,
		DEFAULT_FONT_COLOR,
		DEFAULT_FRAME_COLOR,
		DEFAULT_ARROW_COLOR
	};

      internal bool noLegend = false; // ok
      internal bool onlyGraph = false; // ok
      internal bool forceRulesLegend = false; // ok
      internal String title = null; // ok
      internal long step = 0; // ok
      public Font smallFont = RrdGraphConstants.DEFAULT_SMALL_FONT; // ok
      internal Font largeFont = RrdGraphConstants.DEFAULT_LARGE_FONT; // ok
      public bool drawXGrid = true; // ok
      internal bool drawYGrid = true; // ok
      int firstDayOfWeek = FIRST_DAY_OF_WEEK; // ok
      internal bool showSignature = true;

      internal List<Source> sources = new List<Source>();
      public List<CommentText> comments = new List<CommentText>();
      internal List<PlotElement> plotElements = new List<PlotElement>();

      /**
       * Creates RrdGraphDef object and sets default time span (default ending time is 'now',
       * default starting time is 'end-1day'.
       */
      public RrdGraphDef()
      {
         setStartTime(Util.getTimestamp(DateTime.Now.Subtract(new TimeSpan(1, 0, 0, 0))));
         setEndTime(Util.getTimestamp(DateTime.Now));
      }

      /**
       * Sets the time when the graph should begin. Time in seconds since epoch
       * (1970-01-01) is required. Negative numbers are relative to the current time.
       *
       * @param time Starting time for the graph in seconds since epoch
       */
      public void setStartTime(long time)
      {
         this.startTime = time;
         if (time <= 0)
         {
            this.startTime += Util.getTimestamp();
         }
      }

      /**
       * Sets the time when the graph should end. Time in seconds since epoch
       * (1970-01-01) is required. Negative numbers are relative to the current time.
       *
       * @param time Ending time for the graph in seconds since epoch
       */
      public void setEndTime(long time)
      {
         endTime = time;
         if (time <= 0)
         {
            endTime += Util.getTimestamp();
         }
      }

      /**
       * Sets starting and ending time for the for the graph. Timestamps in seconds since epoch are
       * required. Negative numbers are relative to the current time.
       *
       * @param startTime Starting time in seconds since epoch
       * @param endTime   Ending time in seconds since epoch
       */
      public void setTimeSpan(long startTime, long endTime)
      {
         setStartTime(startTime);
         setEndTime(endTime);
      }

      /**
       * Sets starting and ending time for the for the graph. Timestamps in seconds since epoch are
       * required.
       *
       * @param timestamps Array of timestamps. The first array item will be chosen for the starting
       *                   timestamp. The last array item will be chosen for the ending timestamp.
       */
      public void setTimeSpan(long[] timestamps)
      {
         setTimeSpan(timestamps[0], timestamps[timestamps.Length - 1]);
      }

      /**
       * Sets RrdDbPool usage policy (defaults to true). If set to true,
       * {@link org.Rrd4n.core.RrdDbPool RrdDbPool} will be used to
       * access individual RRD files. If set to false, RRD files will be accessed directly.
       *
       * @param poolUsed true, if RrdDbPool class should be used. False otherwise.
       */
      public void setPoolUsed(bool poolUsed)
      {
         this.poolUsed = poolUsed;
      }

      /**
       * Sets the name of the graph to generate. Since Rrd4n outputs GIFs, PNGs,
       * and JPEGs it's recommended that the filename end in either .gif,
       * .png or .jpg. Rrd4n does not enforce this, however. If the filename is
       * set to '-' the image will be created only in memory (no file will be created).
       * PNG and GIF formats are recommended but JPEGs should be avoided.
       *
       * @param filename Path to the image file
       */
      public void setFilename(String filename)
      {
         this.filename = filename;
      }

      /**
       * Configures x-axis grid and labels. The x-axis label is quite complex to configure.
       * So if you don't have very special needs, you can rely on the autoconfiguration to
       * get this right.
       * <p/>
       * Otherwise, you have to configure three elements making up the x-axis labels
       * and grid. The base grid, the major grid and the labels.
       * The configuration is based on the idea that you first specify a well
       * known amount of time and then say how many times
       * it has to pass between each minor/major grid line or label. For the label
       * you have to define two additional items: The precision of the label
       * in seconds and the format used to generate the text
       * of the label.
       * <p/>
       * For example, if you wanted a graph with a base grid every 10 minutes and a major
       * one every hour, with labels every hour you would use the following
       * x-axis definition.
       * <p/>
       * <pre>
       * setTimeAxis(RrdGraphConstants.MINUTE, 10,
       *             RrdGraphConstants.HOUR, 1,
       *             RrdGraphConstants.HOUR, 1,
       *             0, "%H:%M")
       * </pre>
       * <p/>
       * The precision in this example is 0 because the %X format is exact.
       * If the label was the name of the day, we would have had a precision
       * of 24 hours, because when you say something like 'Monday' you mean
       * the whole day and not Monday morning 00:00. Thus the label should
       * be positioned at noon. By defining a precision of 24 hours or
       * rather 86400 seconds, you make sure that this happens.
       *
       * @param minorUnit        Minor grid unit. Minor grid, major grid and label units
       *                         can be one of the following constants defined in
       *                         {@link RrdGraphConstants}: {@link RrdGraphConstants#SECOND SECOND},
       *                         {@link RrdGraphConstants#MINUTE MINUTE}, {@link RrdGraphConstants#HOUR HOUR},
       *                         {@link RrdGraphConstants#DAY DAY}, {@link RrdGraphConstants#WEEK WEEK},
       *                         {@link RrdGraphConstants#MONTH MONTH}, {@link RrdGraphConstants#YEAR YEAR}.
       * @param minorUnitCount   Number of minor grid units between minor grid lines.
       * @param majorUnit        Major grid unit.
       * @param majorUnitCount   Number of major grid units between major grid lines.
       * @param labelUnit        Label unit.
       * @param labelUnitCount   Number of label units between labels.
       * @param labelSpan        Label precision
       * @param simpleDateFormat Date format (SimpleDateFormat pattern of strftime-like pattern)
       */
      public void setTimeAxis(int minorUnit, int minorUnitCount, int majorUnit, int majorUnitCount,
                        int labelUnit, int labelUnitCount, int labelSpan, String simpleDateFormat)
      {
         timeAxisSetting = new TimeAxisSetting(minorUnit, minorUnitCount, majorUnit, majorUnitCount,
               labelUnit, labelUnitCount, labelSpan, simpleDateFormat);
      }

      /**
       * Sets vertical axis grid and labels. Makes vertical grid lines appear
       * at gridStep interval. Every labelFactor*gridStep, a major grid line is printed,
       * along with label showing the value of the grid line.
       *
       * @param gridStep    Minor grid step
       * @param labelFactor Specifies how many minor minor grid steps will appear between labels
       *                    (major grid lines)
       */
      public void setValueAxis(double gridStep, int labelFactor)
      {
         valueAxisSetting = new ValueAxisSetting(gridStep, labelFactor);
      }

      /**
       * Places Y grid dynamically based on graph Y range. Algorithm ensures
       * that you always have grid, that there are enough but not too many
       * grid lines and the grid is metric. That is grid lines are placed
       * every 1, 2, 5 or 10 units.
       *
       * @param altYGrid true, if Y grid should be calculated dynamically (defaults to false)
       */
      public void setAltYGrid(bool altYGrid)
      {
         this.altYGrid = altYGrid;
      }

      /**
       * Use this method to turn off minor grid lines (printed by default)
       *
       * @param noMinorGrid true, to turn off, false to turn on (default)
       */
      public void setNoMinorGrid(bool noMinorGrid)
      {
         this.noMinorGrid = noMinorGrid;
      }

      /**
       * Use this method to request MRTG-like graph (false by default)
       *
       * @param altYMrtg true, to create MRTG-like graph, false otherwise (default)
       */
      public void setAltYMrtg(bool altYMrtg)
      {
         this.altYMrtg = altYMrtg;
      }

      /**
       * Computes Y range based on function absolute minimum and maximum
       * values. Default algorithm uses predefined set of ranges.  This is
       * good in many cases but it fails miserably when you need to graph
       * something like 260 + 0.001 * sin(x). Default algorithm will use Y
       * range from 250 to 300 and on the graph you will see almost straight
       * line. With --alt-autoscale Y range will be from slightly less the
       * 260 - 0.001 to slightly more then 260 + 0.001 and periodic behavior
       * will be seen.
       *
       * @param altAutoscale true to request alternative autoscaling, false otherwise
       *                     (default).
       */
      public void setAltAutoscale(bool altAutoscale)
      {
         this.altAutoscale = altAutoscale;
      }

      /**
       * Computes Y range based on function absolute minimum and maximum
       * values. Where setAltAutoscale(true) will modify both the absolute maximum AND
       * minimum values, this option will only affect the maximum value. The
       * minimum value, if not defined elsewhere, will be 0. This
       * option can be useful when graphing router traffic when the WAN line
       * uses compression, and thus the throughput may be higher than the
       * WAN line speed.
       *
       * @param altAutoscaleMax true to request alternative autoscaling, false
       *                        otherwise (default)
       */
      public void setAltAutoscaleMax(bool altAutoscaleMax)
      {
         this.altAutoscaleMax = altAutoscaleMax;
      }

      /**
       * Sets the 10**unitsExponent scaling of the y-axis values. Normally
       * values will be scaled to the appropriate units (k, M, etc.). However
       * you may wish to display units always in k (Kilo, 10e3) even if
       * the data is in the M (Mega, 10e6) range for instance.  Value should
       * be an integer which is a multiple of 3 between -18 and 18, inclu-
       * sive. It is the exponent on the units you which to use.  For example,
       * use 3 to display the y-axis values in k (Kilo, 10e3, thou-
       * sands), use -6 to display the y-axis values in u (Micro, 10e-6,
       * millionths). Use a value of 0 to prevent any scaling of the y-axis
       * values.
       *
       * @param unitsExponent
       */
      public void setUnitsExponent(int unitsExponent)
      {
         this.unitsExponent = unitsExponent;
      }

      /**
       * Sets the character width on the left side of the graph for
       * y-axis values.
       *
       * @param unitsLength Number of characters on the left side of the graphs
       *                    reserved for vertical axis labels.
       */
      public void setUnitsLength(int unitsLength)
      {
         this.unitsLength = unitsLength;
      }

      /**
       * Sets vertical label on the left side of the graph. This is normally used
       * to specify the units used.
       *
       * @param verticalLabel Vertical axis label
       */
      public void setVerticalLabel(String verticalLabel)
      {
         this.verticalLabel = verticalLabel;
      }

      /**
       * Sets width of the drawing area within the graph. This affects the total
       * size of the image.
       *
       * @param width Width of the drawing area.
       */
      public void setWidth(int width)
      {
         this.width = width;
      }

      /**
       * Sets height of the drawing area within the graph. This affects the total
       * size of the image.
       *
       * @param height Height of the drawing area.
       */
      public void setHeight(int height)
      {
         this.height = height;
      }

      /**
       * Creates interlaced GIF image (currently not supported,
       * method is present only for RRDTool comaptibility).
       *
       * @param interlaced true, if GIF image should be interlaced.
       */
      public void setInterlaced(bool interlaced)
      {
         this.interlaced = interlaced;
      }

      /**
       * Creates additional image information.
       * After the image has been created, the graph function uses imageInfo
       * format string (printf-like) to create output similar to
       * the {@link #print(String, ConsolFun, String)} function.
       * The format string is supplied with the following parameters:
       * filename, xsize and ysize (in that particular order).
       * <p/>
       * For example, in order to generate an IMG tag
       * suitable for including the graph into a web page, the command
       * would look like this:
       * <pre>
       * setImageInfo(&quot;&lt;IMG SRC='/img/%s' WIDTH='%d' HEIGHT='%d' ALT='Demo'&gt;&quot;);
       * </pre>
       *
       * @param imageInfo Image info format. Use %s placeholder for filename, %d placeholder for
       *                  image width and height.
       */
      public void setImageInfo(String imageInfo)
      {
         this.imageInfo = imageInfo;
      }

      /**
       * Sets image format.
       *
       * @param imageFormat "PNG", "GIF" or "JPG".
       */
      public void setImageFormat(String imageFormat)
      {
         this.imageFormat = imageFormat;
      }

      /**
       * Sets background image - currently, only PNG images can be used as background.
       *
       * @param backgroundImage Path to background image
       */
      public void setBackgroundImage(String backgroundImage)
      {
         this.backgroundImage = backgroundImage;
      }

      /**
       * Sets overlay image - currently, only PNG images can be used as overlay. Overlay image is
       * printed on the top of the image, once it is completely created.
       *
       * @param overlayImage Path to overlay image
       */
      public void setOverlayImage(String overlayImage)
      {
         this.overlayImage = overlayImage;
      }

      /**
       * Sets unit to be displayed on y axis. It is wise to use only short units on graph, however.
       *
       * @param unit Unit description
       */
      public void setUnit(String unit)
      {
         this.unit = unit;
      }

      /**
       * Creates graph only if the current graph is out of date or not existent.
       *
       * @param lazy true, if graph should be 'lazy', false otherwise (defualt)
       */
      public void setLazy(bool lazy)
      {
         this.lazy = lazy;
      }

      /**
       * Sets the lower limit of a graph. But rather, this is the
       * maximum lower bound of a graph. For example, the value -100 will
       * result in a graph that has a lower limit of -100 or less.  Use this
       * method to expand graphs down.
       *
       * @param minValue Minimal value displayed on the graph
       */
      public void setMinValue(double minValue)
      {
         this.minValue = minValue;
      }

      /**
       * Defines the value normally located at the upper border of the
       * graph. If the graph contains higher values, the upper border will
       * move upwards to accommodate these values as well.
       * <p/>
       * If you want to define an upper-limit which will not move in any
       * event you have to use {@link #setRigid(bool)} method as well.
       *
       * @param maxValue Maximal value displayed on the graph.
       */
      public void setMaxValue(double maxValue)
      {
         this.maxValue = maxValue;
      }

      /**
       * Sets rigid boundaries mode. Normally Rrd4n will automatically expand
       * the lower and upper limit if the graph contains a value outside the
       * valid range. With the <code>true</code> argument you can disable this behavior.
       *
       * @param rigid true if uper and lower limits should not be expanded to accomodate
       *              values outside of the specified range. False otherwise (default).
       */
      public void setRigid(bool rigid)
      {
         this.rigid = rigid;
      }

      /**
       * Sets default base for magnitude scaling. If you are graphing memory
       * (and NOT network traffic) this switch should be set to 1024 so that 1Kb is 1024 byte.
       * For traffic measurement, 1 kb/s is 1000 b/s.
       *
       * @param base Base value (defaults to 1000.0)
       */
      public void setBase(double scaleBase)
      {
         this.scaleBase = scaleBase;
      }

      /**
       * Sets logarithmic y-axis scaling.
       *
       * @param logarithmic true, for logarithmic scaling, false otherwise (default).
       */
      public void setLogarithmic(bool logarithmic)
      {
         this.logarithmic = logarithmic;
      }

      /**
       * Overrides the colors for the standard elements of the graph. The
       * colorTag must be one of the following constants defined in the {@link RrdGraphConstants}:
       * {@link RrdGraphConstants#COLOR_BACK COLOR_BACK}ground,
       * {@link RrdGraphConstants#COLOR_CANVAS COLOR_CANVAS},
       * {@link RrdGraphConstants#COLOR_SHADEA COLOR_SHADEA} left/top border,
       * {@link RrdGraphConstants#COLOR_SHADEB COLOR_SHADEB} right/bottom border,
       * {@link RrdGraphConstants#COLOR_GRID COLOR_GRID},
       * {@link RrdGraphConstants#COLOR_MGRID COLOR_MGRID} major grid,
       * {@link RrdGraphConstants#COLOR_FONT COLOR_FONT},
       * {@link RrdGraphConstants#COLOR_FRAME COLOR_FRAME} and axis of the graph or
       * {@link RrdGraphConstants#COLOR_ARROW COLOR_ARROW}. This
       * method can be called multiple times to set several colors.
       *
       * @param colorTag Color tag, as explained above.
       * @param color    Any color (paint) you like
       */
      public void setColor(int colorTag, Color color)
      {
         if (colorTag >= 0 && colorTag < colors.Length)
         {
            colors[colorTag] = color;
         }
         else
         {
            throw new ArgumentException("Invalid color index specified: " + colorTag);
         }
      }

      /**
       * Overrides the colors for the standard elements of the graph by element name.
       * See {@link #setColor(int, java.awt.Color)} for full explanation.
       *
       * @param colorName One of the following strings: "BACK", "CANVAS", "SHADEA", "SHADEB",
       *                  "GRID", "MGRID", "FONT", "FRAME", "ARROW"
       * @param color     Any color (paint) you like
       */
      public void setColor(String colorName, Color color)
      {
         setColor(getColorTagByName(colorName), color);
      }

      private static int getColorTagByName(String colorName)
      {
         for (int i = 0; i < COLOR_NAMES.Length; i++)
         {
            if (COLOR_NAMES[i].ToUpper().CompareTo(colorName) == 0)
            {
               return i;
            }
         }
         throw new ArgumentException("Unknown color name specified: " + colorName);
      }

      /**
       * Suppress generation of legend, only render the graph.
       *
       * @param noLegend true if graph legend should be omitted. False otherwise (default).
       */
      public void setNoLegend(bool noLegend)
      {
         this.noLegend = noLegend;
      }

      /**
       * Suppresses anything but the graph, works only for height < 64.
       *
       * @param onlyGraph true if only graph should be created, false otherwise (default).
       */
      public void setOnlyGraph(bool onlyGraph)
      {
         this.onlyGraph = onlyGraph;
      }

      /**
       * Force the generation of HRULE and VRULE legend even if those HRULE
       * or VRULE will not be drawn because out of graph boundaries.
       *
       * @param forceRulesLegend true if rule legend should be always printed,
       *                         false otherwise (default).
       */
      public void setForceRulesLegend(bool forceRulesLegend)
      {
         this.forceRulesLegend = forceRulesLegend;
      }

      /**
       * Defines a title to be written into the graph.
       *
       * @param title Graph title.
       */
      public void setTitle(String title)
      {
         this.title = title;
      }

      /**
       * Suggests which time step should be used by Rrd4n while processing data from RRD files.
       *
       * @param step Desired time step (don't use this method if you don't know what you're doing).
       */
      public void setStep(long step)
      {
         this.step = step;
      }

      /**
       * Sets default font for graphing. Note that Rrd4n will behave unpredictably if proportional
       * font is selected.
       *
       * @param smallFont Default font for graphing. Use only monospaced fonths.
       */
      public void setSmallFont(Font smallFont)
      {
         this.smallFont = smallFont;
      }

      /**
       * Sets title font.
       *
       * @param largeFont Font to be used for graph title.
       */
      public void setLargeFont(Font largeFont)
      {
         this.largeFont = largeFont;
      }

      /**
       * Defines virtual datasource. This datasource can then be used
       * in other methods like {@link #datasource(String, String)} or
       * {@link #gprint(String, ConsolFun, String)}.
       *
       * @param name      Source name
       * @param rrdPath   Path to RRD file
       * @param dsName    Datasource name in the specified RRD file
       * @param consolFun Consolidation function (AVERAGE, MIN, MAX, LAST)
       */
      public void datasource(String name, String rrdPath, String dsName, ConsolFun consolFun)
      {
         sources.Add(new Def(name, rrdPath, dsName, consolFun));
      }

      // RRDTool Version 1.2
      public void AddDatasource(Def def)
      {
         sources.Add(def);
      }
      /**
       * Defines virtual datasource. This datasource can then be used
       * in other methods like {@link #datasource(String, String)} or
       * {@link #gprint(String, ConsolFun, String)}.
       *
       * @param name      Source name
       * @param rrdPath   Path to RRD file
       * @param dsName    Datasource name in the specified RRD file
       * @param consolFun Consolidation function (AVERAGE, MIN, MAX, LAST)
       * @param backend   Backend to be used while fetching data from a RRD file.
       */
      public void datasource(String name, String rrdPath, String dsName, ConsolFun consolFun, String backend)
      {
         sources.Add(new Def(name, rrdPath, dsName, consolFun, backend));
      }

      /**
       * Create a new virtual datasource by evaluating a mathematical
       * expression, specified in Reverse Polish Notation (RPN).
       *
       * @param name          Source name
       * @param rpnExpression RPN expression.
       */
      public void datasource(String name, String rpnExpression)
      {
         sources.Add(new CDef(name, rpnExpression));
      }

      /**
       * Creates a new (static) virtual datasouce. The value of the datasource is constant. This value is
       * evaluated by applying the given consolidation function to another virtual datasource.
       *
       * @param name      Source name
       * @param defName   Other source name
       * @param consolFun Consolidation function to be applied to other datasource.
       */
      public void datasource(String name, String defName, AggregateFunction.Type function)
      {
         sources.Add(new SDef(name, defName, function));
      }

      /**
       * Creates a new (plottable) datasource. Datasource values are obtained from the given plottable
       * object.
       *
       * @param name      Source name.
       * @param plottable Plottable object.
       */
      public void datasource(String name, rrd4n.Data.Plottable plottable)
      {
         sources.Add(new PDef(name, plottable));
      }

      public void datasource(string name, long shiftOffset)
      {
         sources.Add(new Shift(name, shiftOffset));
      }

      /**
      * Calculates the chosen consolidation function CF over the given datasource
      * and creates the result by using the given format string.  In
      * the format string there should be a '%[l]f', '%[l]g' or '%[l]e' marker in
      * the place where the number should be printed.
      * <p/>
      * If an additional '%s' is found AFTER the marker, the value will be
      * scaled and an appropriate SI magnitude unit will be printed in
      * place of the '%s' marker. The scaling will take the '--base' argu-
      * ment into consideration!
      * <p/>
      * If a '%S' is used instead of a '%s', then instead of calculating
      * the appropriate SI magnitude unit for this value, the previously
      * calculated SI magnitude unit will be used.  This is useful if you
      * want all the values in a print statement to have the same SI magni-
      * tude unit.  If there was no previous SI magnitude calculation made,
      * then '%S' behaves like a '%s', unless the value is 0, in which case
      * it does not remember a SI magnitude unit and a SI magnitude unit
      * will only be calculated when the next '%s' is seen or the next '%S'
      * for a non-zero value.
      * <p/>
      * Print results are collected in the {@link RrdGraphInfo} object which is retrieved
      * from the {@link RrdGraph object} once the graph is created.
      *
      * @param srcName   Virtual source name
      * @param format    Format string (like "average = %10.3f %s")
      */
      public void print(String srcName, String format, bool strftime)
      {
         comments.Add(new PrintText(srcName, format, false, strftime));
      }

      /**
       * This method does basically the same thing as {@link #print(String, ConsolFun, String)},
       * but the result is printed on the graph itself, below the chart area.
       *
       * @param srcName   Virtual source name
       * @param format    Format string (like "average = %10.3f %s")
       */
      // To support new syntax
      public void gprint(String srcName, String format, bool strftime)
      {
         comments.Add(new PrintText(srcName, format, true,strftime));
      }

      /**
       * Comment to be printed on the graph.
       *
       * @param text Comment text
       */
      public void comment(String text)
      {
         comments.Add(new CommentText(text));
      }

      /**
       * Draws a horizontal rule into the graph and optionally adds a legend
       *
       * @param value  Position of the rule
       * @param color  Rule color
       * @param legend Legend text. If null, legend text will be omitted.
       */
      public void hrule(double value, Color color, String legend)
      {
         hrule(value, color, legend, 1.0F);
      }

      /**
       * Draws a horizontal rule into the graph and optionally adds a legend
       *
       * @param value  Position of the rule
       * @param color  Rule color
       * @param legend Legend text. If null, legend text will be omitted.
       * @param width Rule width
       */
      public void hrule(double value, Color color, String legend, float width)
      {
         LegendText legendText = new LegendText(color, legend);
         comments.Add(legendText);
         plotElements.Add(new HRule(value, color, legendText, width));
      }

      /**
       * Draws a vertical rule into the graph and optionally adds a legend
       *
       * @param timestamp Position of the rule (seconds since epoch)
       * @param color     Rule color
       * @param legend    Legend text. Use null to omit the text.
       */
      public void vrule(long timestamp, Color color, String legend)
      {
         vrule(timestamp, color, legend, 1.0F);
      }

      /**
       * Draws a vertical rule into the graph and optionally adds a legend
       *
       * @param timestamp Position of the rule (seconds since epoch)
       * @param color     Rule color
       * @param legend    Legend text. Use null to omit the text.
       * @param width     Rule width
       */
      public void vrule(long timestamp, Color color, String legend, float width)
      {
         LegendText legendText = new LegendText(color, legend);
         comments.Add(legendText);
         plotElements.Add(new VRule(timestamp, color, legendText, width));
      }

      /**
       * Plots requested data as a line, using the color and the line width specified.
       *
       * @param srcName Virtual source name
       * @param color   Line color
       * @param legend  Legend text
       * @param width   Line width (default: 1.0F)
       */
      public void line(String srcName, Color color, String legend, float width)
      {
         LegendText legendText = new LegendText(color, legend);
         comments.Add(legendText);
         plotElements.Add(new Line(srcName, color, width));
      }

      /**
       * Plots requested data as a line, using the color specified. Line width is assumed to be
       * 1.0F.
       *
       * @param srcName Virtual source name
       * @param color   Line color
       * @param legend  Legend text
       */
      public void line(String srcName, Color color, String legend)
      {
         line(srcName, color, legend, 1F);
      }

      /**
       * Plots requested data in the form of the filled area starting from zero, using
       * the color specified.
       *
       * @param srcName Virtual source name.
       * @param color   Color of the filled area.
       * @param legend  Legend text.
       */
      public void area(String srcName, Color color, String legend)
      {
         LegendText legendText = new LegendText(color, legend);
         comments.Add(legendText);
         plotElements.Add(new Area(srcName, color));
      }

      /**
       * Does the same as {@link #line(String, java.awt.Color, String)},
       * but the graph gets stacked on top of the
       * previous LINE, AREA or STACK graph. Depending on the type of the
       * previous graph, the STACK will be either a LINE or an AREA.  This
       * obviously implies that the first STACK must be preceded by an AREA
       * or LINE.
       * <p/>
       * Note, that when you STACK onto *UNKNOWN* data, Rrd4n will not
       * draw any graphics ... *UNKNOWN* is not zero.
       *
       * @param srcName Virtual source name
       * @param color   Stacked graph color
       * @param legend  Legend text
       * @throws ArgumentException Thrown if this STACK has no previously defined AREA, STACK or LINE
       *                      graph bellow it.
       */
      public void stack(String srcName, Color color, String legend)
      {
         // find parent AREA or LINE
         SourcedPlotElement parent = null;
         for (int i = plotElements.Count - 1; i >= 0; i--)
         {
            PlotElement plotElement = plotElements[i];
            if (plotElement.GetType() == typeof(SourcedPlotElement)
               || plotElement.GetType() == typeof(Area)
               || plotElement.GetType() == typeof(Line))
            {
               parent = (SourcedPlotElement)plotElement;
               break;
            }
         }
         if (parent == null)
         {
            throw new ArgumentException("You have to stack graph onto something (line or area)");
         }
         else
         {
            LegendText legendText = new LegendText(color, legend);
            comments.Add(legendText);
            plotElements.Add(new Stack(parent, srcName, color));
         }
      }
      public void Shift(string srcName, long shiftOffset)
      {

      }
      /**
       * Sets visibility of the X-axis grid.
       *
       * @param drawXGrid True if X-axis grid should be created (default), false otherwise.
       */
      public void setDrawXGrid(bool drawXGrid)
      {
         this.drawXGrid = drawXGrid;
      }

      /**
       * Sets visibility of the Y-axis grid.
       *
       * @param drawYGrid True if Y-axis grid should be created (default), false otherwise.
       */
      public void setDrawYGrid(bool drawYGrid)
      {
         this.drawYGrid = drawYGrid;
      }

      /**
       * Sets image quality. Relevant only for JPEG images.
       *
       * @param imageQuality (0F=worst, 1F=best).
       */
      public void setImageQuality(float imageQuality)
      {
         this.imageQuality = imageQuality;
      }

      /**
       * Controls if the chart area of the image should be antialiased or not.
       *
       * @param antiAliasing use true to turn antialiasing on, false to turn it off (default)
       */
      public void setAntiAliasing(bool antiAliasing)
      {
         this.antiAliasing = antiAliasing;
      }

      /**
       * Shows or hides graph signature (gator) in the top right corner of the graph
       * @param showSignature true, if signature should be seen (default), false otherwise
       */
      public void setShowSignature(bool showSignature)
      {
         this.showSignature = showSignature;
      }

      /**
       * Sets first day of the week.
       *
       * @param firstDayOfWeek One of the following constants:
       *                       {@link RrdGraphConstants#MONDAY MONDAY},
       *                       {@link RrdGraphConstants#TUESDAY TUESDAY},
       *                       {@link RrdGraphConstants#WEDNESDAY WEDNESDAY},
       *                       {@link RrdGraphConstants#THURSDAY THURSDAY},
       *                       {@link RrdGraphConstants#FRIDAY FRIDAY},
       *                       {@link RrdGraphConstants#SATURDAY SATURDAY},
       *                       {@link RrdGraphConstants#SUNDAY SUNDAY}
       */
      public void setFirstDayOfWeek(int firstDayOfWeek)
      {
         this.firstDayOfWeek = firstDayOfWeek;
      }

      // helper methods

      public int printStatementCount()
      {
         int count = 0;
         foreach (CommentText comment in comments)
         {
            if (comment.GetType() == typeof(PrintText))
            {
               if (comment.isPrint())
               {
                  count++;
               }
            }
         }
         return count;
      }

      internal bool shouldPlot()
      {
         if (plotElements.Count > 0)
         {
            return true;
         }
         foreach (CommentText comment in comments)
         {
            if (comment.isValidGraphElement())
            {
               return true;
            }
         }
         return false;
      }
   }
}
